package com.lucenex.base;



import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.apache.lucene.analysis.miscellaneous.PerFieldAnalyzerWrapper;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.Sort;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.search.TopFieldCollector;
import org.apache.lucene.search.highlight.Highlighter;
import org.apache.lucene.search.highlight.InvalidTokenOffsetsException;
import org.apache.lucene.search.highlight.QueryScorer;

import com.lucenex.bean.BeanConfig;
import com.lucenex.core.LuceneDataSource;
import com.lucenex.core.LuceneXConfig;
import com.lucenex.util.ObjectKit;
import com.lucenex.util.OpenLuceneSource;
import com.lucenex.util.Page;


public class BaseLuceneXDao {

	LuceneXConfig luceneXConfig;


	public BaseLuceneXDao(){
		luceneXConfig = LuceneDataSource.build().getDataSource(LuceneDataSource.getDatabaseType());
	}

	public void saveIndex(List<Document> list) throws IOException{
		IndexWriter writer = luceneXConfig.getWriter();
		writer.addDocuments(list);
	}
	public void saveIndex(Document doc) throws IOException{
		IndexWriter writer = luceneXConfig.getWriter();
		writer.addDocument(doc);
	}

	public void delAll() throws IOException{
		IndexWriter writer = luceneXConfig.getWriter();
		writer.deleteAll();
		realTime();
	}

	public void deletekey(Term term) throws IOException{
		IndexWriter writer = luceneXConfig.getWriter();
		writer.deleteDocuments(term);
		realTime();
	}
	public void deletekey(Query query) throws IOException{
		IndexWriter writer = luceneXConfig.getWriter();
		writer.deleteDocuments(query);
		realTime();
	}

	public void updateIndex(List<Document> list,Term term) throws IOException{
		IndexWriter writer = luceneXConfig.getWriter();
		writer.updateDocuments(term, list);
	}
	public void updateIndex(List<Document> list,Query query) throws Exception{
		List<Document> dels = findList(query, Document.class, Integer.MAX_VALUE, null);
		if(dels.isEmpty()) {
			throw new Exception("没有要更新的数据");
		}else {
			boolean flag = true;
			try {
				deletekey(query);
				saveIndex(list);//添加新数据
			} catch (Exception e) {
				flag = false;
				throw new Exception("更新失败");
			}finally {
				if(flag) {//更新成功 同步
					realTime();
				}else {//更新失败 回滚
					luceneXConfig.getWriter().rollback();//回滚
				}
			}
		}
	}
	public void updateIndex(Document doc,Query query) throws Exception{
		List<Document> dels = findList(query, Document.class, Integer.MAX_VALUE, null);
		if(dels.isEmpty()) {
			throw new Exception("没有要更新的数据");
		}else {
			boolean flag = true;
			try {
				deletekey(query);
				saveIndex(doc);//添加新数据
			} catch (Exception e) {
				flag = false;
				throw new Exception("更新失败");
			}finally {
				if(flag) {//更新成功 同步
					realTime();
				}else {//更新失败 回滚
					luceneXConfig.getWriter().rollback();//回滚
				}
			}
		}
	}
	public void updateIndex(Document doc,Term term) throws IOException{
		IndexWriter writer = luceneXConfig.getWriter();
		writer.updateDocument(term, doc);
	}

	public <T> List<T> findList(Query query,Class<T> obj,int num,Sort sort) throws IOException, InvalidTokenOffsetsException, InstantiationException, IllegalAccessException, NoSuchFieldException, SecurityException, IllegalArgumentException, InvocationTargetException {
		IndexSearcher searcher = this.luceneXConfig.getSearcher();
		TopDocs rs;
		if(num == 0){
			num = Integer.MAX_VALUE;
		}
		if(sort != null) {
			rs = searcher.search(query,num,sort);
		}else {
			rs = searcher.search(query,num);
		}
		QueryScorer scorer = new QueryScorer(query);  
		return dataHandle(searcher, rs.scoreDocs, scorer,obj);
	}

	public <T> Page<T> findList(Query query, int pageNumber,int pageSize, Class<T> obj,Sort sort) throws IOException, InvalidTokenOffsetsException, InstantiationException, IllegalAccessException, NoSuchFieldException, SecurityException, IllegalArgumentException, InvocationTargetException {
		
		if(pageNumber == 0) {
			pageNumber = 1;
		}
		if(pageSize == 0){
			pageSize = 10;
		}
		if(sort == null){
			sort = new Sort();
		}
		int pageNum = (pageNumber-1) * pageSize;
		IndexSearcher searcher = this.luceneXConfig.getSearcher(); 
		TopFieldCollector create = TopFieldCollector.create(sort, pageNum+pageSize, false, false, false);
		searcher.search(query,create);
		int totalHits = create.getTotalHits();
		ScoreDoc[] scoreDocs = create.topDocs(pageNum,pageSize).scoreDocs;
		QueryScorer scorer = new QueryScorer(query);
		List<T> dataHandle = dataHandle(searcher, scoreDocs, scorer, obj);
		int totalPage = (int) (totalHits / pageSize);
		if (totalHits % pageSize != 0) {
			totalPage++;
		}
		return new Page<T>(dataHandle, pageNumber, pageSize, totalPage, totalHits);
	}

	public <T> List<T> dataHandle(IndexSearcher searcher,ScoreDoc[] scoreDocs,QueryScorer scorer,Class<T> obj) throws IOException, InvalidTokenOffsetsException, InstantiationException, IllegalAccessException, NoSuchFieldException, SecurityException, IllegalArgumentException, InvocationTargetException{
		Highlighter highlighter = null;
		PerFieldAnalyzerWrapper analyzer = null;
		if(this.luceneXConfig.isHighlight()) {
			analyzer = luceneXConfig.getAnalyzer();
			Map<String, Object> high = BeanConfig.build().getHigh(LuceneDataSource.getDatabaseType());
			Object method = high.get("method");
			if(method != null) {
				Method m = (Method) method;
				highlighter = (Highlighter) m.invoke(high.get("object"), scorer);
			}else {
				//没有设置高亮
				this.luceneXConfig.setHighlight(false);
				System.out.println("没有设置高亮 Highlighter");
			}
		}
		int length = scoreDocs.length;
		List<T> list = new ArrayList<>();
		for (int i = 0; i < length; i++) {
			Document doc = searcher.doc(scoreDocs[i].doc);
			if(obj.equals(Document.class)) {//如果是原版返回 就不再转换了
				list.add((T) doc);
				continue;
			}
			if(this.luceneXConfig.isHighlight()) {
				list.add(ObjectKit.object(doc, obj, highlighter, analyzer));
			}else {
				list.add(ObjectKit.object(doc, obj));
			}
		}
		return list;
	}

	/**
	 * 实时性
	 */
	public synchronized void realTime() {
		LuceneXConfig config = this.luceneXConfig;
		try {
			config.setSearcher(OpenLuceneSource.dynamicIndexSearcher(config.getWriter()));
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
